﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Game.geodata;
using Game.model.@base;
using Game.utils;
using ikvm.extensions;
using java.lang;
using java.util;
using java.util.concurrent.atomic;
using lineage2.commons.lang.reference;
using org.slf4j;
using Math = java.lang.Math;
using Object = System.Object;
using String = System.String;

namespace Game.model
{
    public abstract class GameObject:EventOwner
    {
        private static long serialVersionUID = -950375486287118921L;
	    /**
	     * Field _log.
	     */
	    private static Logger _log = LoggerFactory.getLogger(typeof (GameObject));
	    /**
	     * Field EMPTY_L2OBJECT_ARRAY.
	     */
	    public static GameObject[] EMPTY_L2OBJECT_ARRAY = new GameObject[0];
	    /**
	     * Field CREATED. (value is 0)
	     */
	    protected static int CREATED = 0;
	    /**
	     * Field VISIBLE. (value is 1)
	     */
	    protected static int VISIBLE = 1;
	    /**
	     * Field DELETED. (value is -1)
	     */
	    protected static int DELETED = -1;
	    /**
	     * Field objectId.
	     */
	    protected int objectId;
	    /**
	     * Field _x.
	     */
	    private int _x;
	    /**
	     * Field _y.
	     */
	    private int _y;
	    /**
	     * Field _z.
	     */
	    private int _z;
	    /**
	     * Field _reflection.
	     */
	    protected Reflection _reflection = ReflectionManager.DEFAULT;
	    /**
	     * Field _currentRegion.
	     */
	    private WorldRegion _currentRegion;
	    /**
	     * Field _state.
	     */
	    private AtomicInteger _state = new AtomicInteger(CREATED);
	
	    /**
	     * Constructor for GameObject.
	     */
        protected GameObject()
        {
        }

        /**
         * Constructor for GameObject.
         * @param objectId int
         */
        public GameObject(int objectId)
        {
            this.objectId = objectId;
        }
        public HardReference getRef()
	    {
		    return HardReferences.emptyRef();
	    }
        private void clearRef()
	    {
		    HardReference reference = getRef();
		    if (reference != null)
		    {
			    reference.clear();
		    }
	    }
        public Reflection getReflection()
        {
            return _reflection;
        }
        public int getReflectionId()
        {
            return _reflection.getId();
        }
        public int getGeoIndex()
        {
            return _reflection.getGeoIndex();
        }
        public void setReflection(Reflection reflection)
        {
            if (_reflection == reflection)
            {
                return;
            }
            bool respawn = false;
            if (isVisible())
            {
                decayMe();
                respawn = true;
            }
            Reflection r = getReflection();
            if (!r.isDefault())
            {
                r.removeObject(this);
            }
            _reflection = reflection;
            if (!reflection.isDefault())
            {
                reflection.addObject(this);
            }
            if (respawn)
            {
                spawnMe();
            }
        }
        public void setReflection(int reflectionId)
	    {
		    Reflection r = ReflectionManager.getInstance().get(reflectionId);
		    if (r == null)
		    {
			    Log.debug("Trying to set unavailable reflection: " + reflectionId + " for object: " + this + "!", new Throwable().fillInStackTrace());
			    return;
		    }
		    setReflection(r);
	    }
	    public int hashCode()
	    {
		    return objectId;
	    }
	    public int getObjectId()
	    {
		    return objectId;
	    }
        public int getX()
	    {
		    return _x;
	    }
	    public int getY()
	    {
		    return _y;
	    }
	    public int getZ()
	    {
		    return _z;
	    }
	    public Location getLoc()
	    {
		    return new Location(_x, _y, _z, getHeading());
	    }
        public int getGeoZ(Location loc)
	    {
		    return GeoEngine.getHeight(loc, getGeoIndex());
	    }
	    public void setLoc(Location loc)
	    {
		    setXYZ(loc.x, loc.y, loc.z);
	    }
	    public void setXYZ(int x, int y, int z)
	    {
		    _x = World.validCoordX(x);
		    _y = World.validCoordY(y);
		    _z = World.validCoordZ(z);
		    World.addVisibleObject(this, null);
	    }
	    public bool isVisible()
	    {
		    return _state.get() == VISIBLE;
	    }
	    public InvisibleType getInvisibleType()
	    {
		    return InvisibleType.NONE;
	    }
	    public bool isInvisible()
	    {
		    return getInvisibleType() != InvisibleType.NONE;
	    }
        public void spawnMe(Location loc)
	    {
		    spawnMe0(loc, null);
	    }
	    protected void spawnMe0(Location loc, Creature dropper)
	    {
		    _x = loc.x;
		    _y = loc.y;
		    _z = getGeoZ(loc);
		    spawn0(dropper);
	    }
	    public void spawnMe()
	    {
		    spawn0(null);
	    }
	    protected void spawn0(Creature dropper)
	    {
		    if (!_state.compareAndSet(CREATED, VISIBLE))
		    {
			    return;
		    }
		    World.addVisibleObject(this, dropper);
		    onSpawn();
	    }
        public void toggleVisible()
	    {
		    if (isVisible())
		    {
			    decayMe();
		    }
		    else
		    {
			    spawnMe();
		    }
	    }
	    protected void onSpawn()
	    {
	    }
	    public void decayMe()
	    {
		    if (!_state.compareAndSet(VISIBLE, CREATED))
		    {
			    return;
		    }
		    World.removeVisibleObject(this);
		    onDespawn();
	    }
	    protected void onDespawn()
	    {
	    }
        public void deleteMe()
	    {
		    decayMe();
		    if (!_state.compareAndSet(CREATED, DELETED))
		    {
			    return;
		    }
		    onDelete();
	    }
	    public bool isDeleted()
	    {
		    return _state.get() == DELETED;
	    }
	    protected void onDelete()
	    {
		    Reflection r = getReflection();
		    if (!r.isDefault())
		    {
			    r.removeObject(this);
		    }
		    clearRef();
	    }
	    public void onForcedAttack(Player player, bool shift)
	    {
		    player.sendActionFailed();
	    }
        public bool isAttackable(Creature attacker)
	    {
		    return false;
	    }
	    public String getL2ClassShortName()
	    {
	        return this.getClass().getSimpleName();
	    }
	    public long getXYDeltaSq(int x, int y)
	    {
		    long dx = x - getX();
		    long dy = y - getY();
		    return (dx * dx) + (dy * dy);
	    }
	    public long getXYDeltaSq(Location loc)
	    {
		    return getXYDeltaSq(loc.x, loc.y);
	    }
	    public long getZDeltaSq(int z)
	    {
		    long dz = z - getZ();
		    return dz * dz;
	    }
	    public long getZDeltaSq(Location loc)
	    {
		    return getZDeltaSq(loc.z);
	    }
        public long getXYZDeltaSq(int x, int y, int z)
	    {
		    return getXYDeltaSq(x, y) + getZDeltaSq(z);
	    }
	    public long getXYZDeltaSq(Location loc)
	    {
		    return getXYDeltaSq(loc.x, loc.y) + getZDeltaSq(loc.z);
	    }
	    public double getDistance(int x, int y)
	    {
		    return Math.sqrt(getXYDeltaSq(x, y));
	    }
	    public double getDistance(int x, int y, int z)
	    {
		    return Math.sqrt(getXYZDeltaSq(x, y, z));
	    }
        public double getDistance(Location loc)
	    {
		    return getDistance(loc.x, loc.y, loc.z);
	    }
	    public bool isInRange(GameObject obj, long range)
	    {
		    if (obj == null)
		    {
			    return false;
		    }
		    if (obj.getReflection() != getReflection())
		    {
			    return false;
		    }
		    long dx = Math.abs(obj.getX() - getX());
		    if (dx > range)
		    {
			    return false;
		    }
		    long dy = Math.abs(obj.getY() - getY());
		    if (dy > range)
		    {
			    return false;
		    }
		    long dz = Math.abs(obj.getZ() - getZ());
		    return (dz <= 1500) && (((dx * dx) + (dy * dy)) <= (range * range));
	    }
        public bool isInRangeZ(GameObject obj, long range)
	    {
		    if (obj == null)
		    {
			    return false;
		    }
		    if (obj.getReflection() != getReflection())
		    {
			    return false;
		    }
		    long dx = Math.abs(obj.getX() - getX());
		    if (dx > range)
		    {
			    return false;
		    }
		    long dy = Math.abs(obj.getY() - getY());
		    if (dy > range)
		    {
			    return false;
		    }
		    long dz = Math.abs(obj.getZ() - getZ());
		    return (dz <= range) && (((dx * dx) + (dy * dy) + (dz * dz)) <= (range * range));
	    }
        public bool isInRange(Location loc, long range)
	    {
		    return isInRangeSq(loc, range * range);
	    }
	    public bool isInRangeSq(Location loc, long range)
	    {
		    return getXYDeltaSq(loc) <= range;
	    }
	    public bool isInRangeZ(Location loc, long range)
	    {
		    return isInRangeZSq(loc, range * range);
	    }
	    public bool isInRangeZSq(Location loc, long range)
	    {
		    return getXYZDeltaSq(loc) <= range;
	    }
	    public double getDistance(GameObject obj)
	    {
		    if (obj == null)
		    {
			    return 0;
		    }
		    return Math.sqrt(getXYDeltaSq(obj.getX(), obj.getY()));
	    }
        public double getDistance3D(GameObject obj)
	    {
		    if (obj == null)
		    {
			    return 0;
		    }
		    return Math.sqrt(getXYZDeltaSq(obj.getX(), obj.getY(), obj.getZ()));
	    }
	    public double getRealDistance(GameObject obj)
	    {
		    return getRealDistance3D(obj, true);
	    }
	    public double getRealDistance3D(GameObject obj)
	    {
		    return getRealDistance3D(obj, false);
	    }
	    public double getRealDistance3D(GameObject obj, bool ignoreZ)
	    {
		    double distance = ignoreZ ? getDistance(obj) : getDistance3D(obj);
		    if (isCreature())
		    {
			    distance -= ((Creature) this).getTemplate().getCollisionRadius();
		    }
		    if (obj.isCreature())
		    {
			    distance -= ((Creature) obj).getTemplate().getCollisionRadius();
		    }
		    return distance > 0 ? distance : 0;
	    }
        public long getSqDistance(int x, int y)
	    {
		    return getXYDeltaSq(x, y);
	    }
	    public long getSqDistance(GameObject obj)
	    {
		    if (obj == null)
		    {
			    return 0;
		    }
		    return getXYDeltaSq(obj.getLoc());
	    }
	    public Player getPlayer()
	    {
		    return null;
	    }
	    public int getHeading()
	    {
		    return 0;
	    }
	    public int getMoveSpeed()
	    {
		    return 0;
	    }
        public WorldRegion getCurrentRegion()
        {
            return _currentRegion;
        }
        public void setCurrentRegion(WorldRegion region)
        {
            _currentRegion = region;
        }
        public bool isInObserverMode()
        {
            return false;
        }
        public bool isInOlympiadMode()
        {
            return false;
        }
        public bool isInBoat()
        {
            return false;
        }
        public bool isInShuttle()
        {
            return false;
        }
        public bool isFlying()
        {
            return false;
        }
        public double getColRadius()
        {
            _log.warn("getColRadius called directly from L2Object");
            Thread.dumpStack();
            return 0;
        }
        public double getColHeight()
        {
            _log.warn("getColHeight called directly from L2Object");
            Thread.dumpStack();
            return 0;
        }
        public bool isCreature()
        {
            return false;
        }
        public bool isPlayable()
        {
            return false;
        }
        public bool isPlayer()
        {
            return false;
        }
        public bool isPet()
        {
            return false;
        }
        public bool isServitor()
        {
            return false;
        }
        public bool isClone()
        {
            return false;
        }
        public bool isNpc()
        {
            return false;
        }
        public bool isMonster()
        {
            return false;
        }
        public bool isItem()
        {
            return false;
        }
        public bool isRaid()
        {
            return false;
        }
        public bool isBoss()
        {
            return false;
        }
        public bool isTrap()
        {
            return false;
        }
        public bool isDoor()
        {
            return false;
        }
        public bool isArtefact()
        {
            return false;
        }
        public bool isSiegeGuard()
        {
            return false;
        }
        public bool isClanAirShip()
        {
            return false;
        }
        public bool isAirShip()
        {
            return false;
        }
        public bool isBoat()
        {
            return false;
        }
        public bool isVehicle()
        {
            return false;
        }
        public bool isShuttle()
        {
            return false;
        }
        public bool isMinion()
        {
            return false;
        }
        public String getName()
        {
            return this.getClass().getSimpleName() + ":" + objectId;
        }
        public String dump()
        {
            return dump(true);
        }
        public String dump(bool simpleTypes)
	    {
		    return Util.dumpObject(this, simpleTypes, true, true);
	    }
	    public List addPacketList(Player forPlayer, Creature dropper)
	    {
		    return Collections.emptyList();
	    }
	    public List deletePacketList()
	    {
		    return Collections.singletonList(new DeleteObject(this));
	    }
	    
	    public override void addEvent(GlobalEvent even)
	    {
		    even.onAddEvent(this);
		    base.addEvent(even);
	    }
	    public override void removeEvent(GlobalEvent even)
	    {
		    even.onRemoveEvent(this);
		    base.removeEvent(even);
	    }
        
	    public override bool equals(Object obj)
	    {
		    if (obj == this)
		    {
			    return true;
		    }
		    if (obj == null)
		    {
			    return false;
		    }
		    if (obj.getClass() != this.getClass())
		    {
			    return false;
		    }
		    return ((GameObject) obj).getObjectId() == getObjectId();
	    }

	    public void onActionTarget(Player player, bool forced)
	    {
		    player.setTarget(this);
	    }
        public void onActionSelect(Player player, bool forced)
	    {
		    if(Events.onAction(player, this, forced))
		    {
			    return;
		    }

		    if(player.getTarget() == this)
		    {
			    onActionTargeted(player, forced);
		    }
		    else
		    {
			    onActionTarget(player, forced);
		    }
	    }

	    public void onActionTargeted(Player player, bool forced)
	    {
		    if(player == this)
		    {
			    return;
		    }
		    if(player.isSitting())
		    {
			    // msg?
			    return;
		    }
		    if(player.isMovementDisabled())
		    {
			    return;
		    }

		    if(!player.isInRange(this, Creature.INTERACTION_DISTANCE))
		    {
			    player.getAI().setIntention(CtrlIntention.AI_INTENTION_INTERACT, this, null);
			    return;
		    }
		    onInteract(player);
	    }

	    public void onInteract(Player player)
	    {
		    // dummy
	    }

	    public bool isTargetable(Player player)
	    {
		    if(player.isInObserverMode())
		    {
			    return false;
		    }

		    if(player.isTeleporting())
		    {
			    return false;
		    }

		    if(player.isOutOfControl())
		    {
			    return false;
		    }

		    if(player.isAlikeDead() && this != player)
		    {
			    return false;
		    }

		    if(player.getReflectionId() != getReflectionId())
		    {
			    return false;
		    }

		    if(player.isLockedTarget())
		    {
			    if(player.isClanAirShipDriver())
			    {

                    /*
                     * 2740 : This action is prohibited while steering.
                     */
				    player.sendPacket(new SystemMessage(SystemMessage.THIS_ACTION_IS_PROHIBITED_WHILE_CONTROLLING));
			    }

			    return false;
		    }

		    if(player.isFrozen())
		    {

                /*
                 * 687 : You cannot move while frozen. Please wait.
                 */
			    player.sendPacket(new SystemMessage(SystemMessage.YOU_CANNOT_MOVE_IN_A_FROZEN_STATE_PLEASE_WAIT_A_MOMENT));
			    return false;
		    }

		    if((player.getAggressionTarget() != null) && (player.getAggressionTarget() != this) && !player.getAggressionTarget().isDead())
		    {
			    return false;
		    }
		    return true;
	    }

	    public bool displayHpBar()
	    {
		    return false;
	    }
    }
}
